package com.roger.homecenterscreen.voice

import java.io.File
import java.io.RandomAccessFile
import java.nio.ByteBuffer
import java.nio.ByteOrder

class WavFileWriter(private val file: File, private val sampleRate: Int) {
    private var raf: RandomAccessFile? = null
    private var dataBytes: Int = 0

    fun open() {
        file.parentFile?.mkdirs()
        raf = RandomAccessFile(file, "rw")
        raf?.setLength(0)
        writeHeader(0)
    }

    fun appendBytes(bytes: ByteArray, offset: Int = 0, length: Int = bytes.size) {
        val r = raf ?: return
        r.seek(r.length())
        r.write(bytes, offset, length)
        dataBytes += length
        updateSizes()
    }

    fun appendShorts(pcm: ShortArray) {
        val bb = ByteBuffer.allocate(pcm.size * 2).order(ByteOrder.LITTLE_ENDIAN)
        pcm.forEach { bb.putShort(it) }
        val arr = bb.array()
        val r = raf ?: return
        r.seek(r.length())
        r.write(arr)
        dataBytes += arr.size
        updateSizes()
    }

    fun close() {
        raf?.close()
        raf = null
    }

    private fun writeHeader(dataLen: Int) {
        val channels = 1
        val bitsPerSample = 16
        val byteRate = sampleRate * channels * bitsPerSample / 8
        val blockAlign = channels * bitsPerSample / 8
        val header = ByteBuffer.allocate(44).order(ByteOrder.LITTLE_ENDIAN)
        header.put("RIFF".toByteArray())
        header.putInt(36 + dataLen)
        header.put("WAVE".toByteArray())
        header.put("fmt ".toByteArray())
        header.putInt(16)
        header.putShort(1)
        header.putShort(channels.toShort())
        header.putInt(sampleRate)
        header.putInt(byteRate)
        header.putShort(blockAlign.toShort())
        header.putShort(bitsPerSample.toShort())
        header.put("data".toByteArray())
        header.putInt(dataLen)
        raf?.seek(0)
        raf?.write(header.array())
    }

    private fun updateSizes() {
        raf?.seek(4)
        raf?.write(intToLe(36 + dataBytes))
        raf?.seek(40)
        raf?.write(intToLe(dataBytes))
    }

    private fun intToLe(v: Int): ByteArray {
        val bb = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
        bb.putInt(v)
        return bb.array()
    }
}